﻿#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

namespace JH
{
    /// <summary>
    ///  曲线图表渲染接口
    /// </summary>
    public class Vector2LineRender : BasicGraphicRender<Vector2>
    {
        /// <summary>
        ///  线宽，单位像素
        /// </summary>
        [SerializeField]
#if ODIN_INSPECTOR
        [LabelText("线宽")]
#endif
        private float lineWidth;

        /// <summary>
        ///  四边形缓存，用于减少内存分配
        ///  顺序为bottom left, top left, top right, bottom right.
        /// </summary>
        private readonly UIVertex[] _quadCache = new UIVertex[4];

        /// <summary>
        ///  边界处理器
        /// </summary>
        [SerializeField] [HideInInspector] private BoundaryHandler boundaryHandler;

        /// <summary>
        ///  获取边界处理器
        /// </summary>
        private BoundaryHandler BoundaryHandler => boundaryHandler;

        /// <summary>
        ///  是否全量渲染
        /// </summary>
        [SerializeField]
#if ODIN_INSPECTOR
        [LabelText("全量渲染")]
        [Tooltip("如果开启全量渲染，则不进行降采样，直接渲染所有数据点。")]
#endif
        private bool isFullRendering = false;

        /// <summary>
        ///  降采样的目标数量
        /// </summary>
        [SerializeField]
#if ODIN_INSPECTOR
        [LabelText("降采样目标数量")]
#endif
        private int targetVertexCount = 500;

        /// <summary>
        ///  没有线宽的顶点列表
        /// </summary>
        [SerializeField]
#if ODIN_INSPECTOR
        [ReadOnly]
        [LabelText("没有线宽的顶点列表")]
#endif
        private List<LineVertexInfo> noWidthVertexList;

        /// <summary>
        ///  获取没有线宽的顶点列表
        /// </summary>
        public IReadOnlyList<LineVertexInfo> NoWidthVertexList => noWidthVertexList;

        /// <summary>
        ///  降采样后的数据点列表
        /// </summary>
        private List<Vector2> _downSampleData;
        
        /// <summary>
        ///  获取要渲染的原始数据
        /// </summary>
        private IReadOnlyList<Vector2> GetRenderData()
        {
            if (isFullRendering)
            {
                // 如果开启全量渲染，则不进行降采样，直接渲染所有数据点
                return BoundaryHandler.SortByX.SortedData;
            }

            // 获取按照X排序后的数据
            _downSampleData ??= new List<Vector2>(targetVertexCount);
            _downSampleData.Clear();

            // 如果边界没有初始化，返回空数据
            if (BoundaryHandler?.SortByX?.SortedData == null) return _downSampleData;

            // 非全量渲染，但原始数据点数量小于等于目标顶点数量时，直接返回原始数据
            if (BoundaryHandler.SortByX.SortedData.Count <= _downSampleData.Count)
            {
                // 如果已经有足够的数据点，直接返回
                return BoundaryHandler.SortByX.SortedData;
            }

            // 使用LTTB算法进行降采样
            var origin = BoundaryHandler.SortByX.SortedData;
            LttbUtility.LttbDownSample(origin, targetVertexCount, _downSampleData);
            return _downSampleData;
        }

        /// <summary>
        ///  渲染线条
        /// </summary>
        protected override void OnUpdateRender()
        {
            base.OnUpdateRender();

            // 获取需要渲染的数据
            var data = GetRenderData();

            // 转换为数据点
            InitNoWidthVertexList(data);
        }

        protected override void OnPopulateMesh(VertexHelper vh)
        {
            vh.Clear();

            // 如果没有数据，或者顶点数量小于2，直接返回
            if (noWidthVertexList == null || noWidthVertexList.Count < 2) return;

            // 初始化获取UV的委托
            for (int i = 0; i < 4; i++) _quadCache[i] = default;

            //获取最开始的点以及偏移点
            var (a, b) = noWidthVertexList[0].GetCurrentAndNormalOffset(lineWidth);

            //从第一个点开始遍历
            for (var i = 1; i < noWidthVertexList.Count; i++)
            {
                var each = noWidthVertexList[i];

                //如果没有夹角了，说明到了最后一个点
                if (each.Angle == null)
                {
                    // 获取最后一个点以及偏移点
                    var (c, d) = each.GetCurrentAndNormalOffset(lineWidth, true);

                    // 添加四边形
                    AddVertexQuad(vh, in a, in b, in c, in d);
                }
                else
                {
                    //如果夹角>addQuadMoreThan，说明需要多添加一个四边形
                    if (each.Angle > Mathf.PI)
                    {
                        // 先缝合四边形
                        var (c, d) = each.GetCurrentAndNormalOffset(lineWidth, true);
                        AddVertexQuad(vh, in a, in b, in c, in d);

                        // 重新定义前一个点和偏移点
                        a = c;
                        b = d;

                        // 获取当前点的计算点和右侧法线偏移点
                        (c, d) = each.GetAngleBisectorAndRightOffset(lineWidth);

                        //添加补充四边形
                        AddVertexQuad(vh, in a, in b, in c, in d);

                        //重新定义偏移点
                        b = d;
                    }
                    else if (each.Angle == 0)
                    {
                        // 夹角为0度，忽略掉这个点
                    }
                    // 夹角小于等于180度，不用填充额外三角形
                    else
                    {
                        // 获取右下
                        var (c, d) = each.GetCurrentAndAngleBisectorOffset(lineWidth);

                        // 添加四边形
                        AddVertexQuad(vh, in a, in b, in c, in d);

                        a = c;
                        b = d;
                    }
                }
            }
        }

        /// <summary>
        ///  添加一个四边形
        /// </summary>
        private void AddVertexQuad(VertexHelper vh, in UIVertex a, in UIVertex b, in UIVertex c, in UIVertex d)
        {
            // 排序,顺序为bottom left, top left, top right, bottom right,存储在_quadCache中
            SortRectPoints(in a, in b, in c, in d);

            // 添加四边形
            vh.AddUIVertexQuad(_quadCache);
        }

        /// <summary>
        ///  初始化没有宽度的顶点列表
        /// </summary>
        /// <param name="origin">原始数据点</param>
        private void InitNoWidthVertexList(IReadOnlyList<Vector2> origin)
        {
            // 清空之前的数据
            noWidthVertexList ??= new List<LineVertexInfo>(((FixedBasicChart<Vector2>)Chart).MaxSize);
            noWidthVertexList.Clear();

            // 逐个处理原始数据点
            for (int i = 0; i < origin.Count; i++)
            {
                //实例化一个顶点信息
                var vertex = new LineVertexInfo
                {
                    BoundaryHandler = BoundaryHandler,
                    Color = color,
                    OriginPoint = origin[i],
                };

                if (i > 0)
                {
                    // 拿到前一个副本
                    var prev = noWidthVertexList[i - 1];

                    // 修改前一个的右向量
                    prev.RightDir = (vertex.OSPosition - prev.OSPosition).normalized;

                    // 写回List
                    noWidthVertexList[i - 1] = prev;

                    // 计算左向量
                    vertex.LeftDir = (prev.OSPosition - vertex.OSPosition).normalized;
                }
                else
                {
                    vertex.LeftDir = null;
                }

                // 把当前顶点添加到列表中
                noWidthVertexList.Add(vertex);
            }

            // 如果没有数据，直接返回
            if (noWidthVertexList.Count == 0) return;

            // 获取最后一个顶点的副本
            var lastVertex = noWidthVertexList[^1];

            // 设置最后一个顶点的右向量为null
            lastVertex.RightDir = null;

            // 写回最后一个顶点
            noWidthVertexList[^1] = lastVertex;
        }


        /// <summary>
        /// 将4个UIVertex按bottom left, top left, top right, bottom right顺序排序到_quadCache，
        /// </summary>
        private void SortRectPoints(in UIVertex a, in UIVertex b, in UIVertex c, in UIVertex d)
        {
            // 先把4个点放到_quadCache中
            _quadCache[0] = a;
            _quadCache[1] = b;
            _quadCache[2] = c;
            _quadCache[3] = d;

            // 手动排序，零分配
            // 先按x排序，从小到大，得到左右两组
            for (int i = 0; i < 4; ++i)
            {
                for (int j = i + 1; j < 4; ++j)
                {
                    if (_quadCache[j].position.x < _quadCache[i].position.x)
                    {
                        // 交换
                        (_quadCache[i], _quadCache[j]) = (_quadCache[j], _quadCache[i]);
                    }
                }
            }

            // 经过x排序后，_quadCache[0,1]为左侧，_quadCache[2,3]为右侧
            UIVertex left0 = _quadCache[0];
            UIVertex left1 = _quadCache[1];
            UIVertex right0 = _quadCache[2];
            UIVertex right1 = _quadCache[3];

            // 左侧两个点，按y排序，y小的是bottom left，y大的是top left
            if (left0.position.y < left1.position.y)
            {
                _quadCache[0] = left0; // bottom left
                _quadCache[1] = left1; // top left
            }
            else
            {
                _quadCache[0] = left1; // bottom left
                _quadCache[1] = left0; // top left
            }

            // 右侧两个点，按y排序，y小的是bottom right，y大的是top right
            if (right0.position.y < right1.position.y)
            {
                _quadCache[2] = right1; // top right
                _quadCache[3] = right0; // bottom right
            }
            else
            {
                _quadCache[2] = right0; // top right
                _quadCache[3] = right1; // bottom right
            }
        }

#if UNITY_EDITOR
        protected override void OnValidate()
        {
            boundaryHandler = this.GetChart<Vector2>().GetChartComp<BoundaryHandler, Vector2>();
            lineWidth = Mathf.Max(0, lineWidth);
            base.OnValidate();
        }
#endif
    }
}